package patternmining.model.construction;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**Implements an index for the members of all the registered packages.
 * Permits to obtain a list of the qualified names of all the types defiened in a package, provided
 * that the types have been registered before.
 * 
 * Types defiened in sources that are part of the java project analyzed have to be registered using operation
 * void addType( String packageName, String typeName)
 * 
 * Types that are provided by the Project Dependencies such as the members of java.lang or any package of a dependency jar
 * are automatically registered
 * 
 * Class is implemented as a Singelton to guarantee that all variables of tyep PackageIndex may refer to the same package index
 * valid for the whole project
 * 
 * 
 * Usage:
 * instance.addType("myPackage.sub", "ClassX"); instance.add("myPackage.sub", "ClassY");
 * Set<String> members = instance.getMembersOfPackage("myPackage.sub"); // members would be { "ClassX", "ClassY" }
 * Set<String> members = instance.getMembersOfPackage("java.lang"); // members would be { "String", "Integer",... and all types of java.lang }
 * 
 * @version 1.0, 04.07.2008
 * @author Holger Wiehen
 */
class PackageIndex {

    private static PackageIndex uniqueInstance = null;
    
    private Map<String , Set<String> > packages;
    private ProjectDependencies dependencyData;
    
    private PackageIndex(){
        packages = new HashMap<String, Set<String> >();
        dependencyData = ProjectDependencies.getInstance();
    }
    
    static PackageIndex getInstance(){
        if( uniqueInstance == null )    uniqueInstance = new PackageIndex();
        return uniqueInstance;
    }
    
    /**Adds the supplied member (type) to the index of members of the supplied package
     * 
     * @param packageName
     * @param memberName the SIMPLE name of the type ex. String, ClassX
     */
    void addType(String packageName, String memberName){
        String qualifiedName = packageName + "." + memberName;
        if( ! packages.containsKey(packageName) )   packages.put(packageName, new HashSet<String>() );
        packages.get(packageName).add(qualifiedName);
    }
    
    /**Returns the fully qualified names of the members of the package
     * 
     * @param packageName
     * @return
     */
    Set<String> getMembersOfPackage(String packageName) throws UnknownPackageException{
        if( !packages.containsKey(packageName) && ! dependencyData.containsPackage(packageName) )   throw new UnknownPackageException();
        // if package is not allready in the list, pull the set of contained types from the project dependencies and store it now in the list
        // permits to return to the caller the requested data and following requests to package members can be answered with less processing
        // (lazy strategy of list completition with the packages that are added through dependencies)
        if( ! packages.containsKey(packageName) ) packages.put(packageName, dependencyData.getTypesOfPackage(packageName) );
        return packages.get(packageName);
    }
    
    boolean existsType(String typeName){
        if ( typeName.lastIndexOf('.') == -1 )  return false;
        String packageName = typeName.substring(0, typeName.lastIndexOf('.') );
        if ( !packages.containsKey(packageName) )   return false;
        return packages.get(packageName).contains( typeName );
    }
    
    boolean existsPackage(String packageName){
        if ( packages.containsKey(packageName) )   return true;
        return false;
    }
    
}

class UnknownPackageException extends Exception{
    
}
